﻿using System;
using System.Collections.Generic;
using System.Data;
using System.Drawing;
using System.Globalization;
using System.Linq;
using DevExpress.Spreadsheet;
using DevExpress.Web;
using DevExpress.Web.ASPxSpreadsheet;
using SpreadSheetFramework.Util.Validate;

namespace SpreadSheetFramework.Util
{
    public abstract class SpreadSheetTemplate
    {
        protected const string TEMP_SHEET = "tempSheetFIU1234@4321";
        protected Color InputCellColor;
        private ASPxSpreadsheet _tempSpreadsheet;

        protected SpreadSheetTemplate()
        {
            InitPrivate();
        }

        public Dictionary<string, ObjInputRowRange> InputRangeDict { get; set; }
        public Dictionary<Color, ObjValidateBase> FontColorValidateDict { get; set; }
        public string Name { get; set; }
        protected string FilePath { get; set; }
        public SpreadSheetTemplateType Type { get; protected set; }

        public Dictionary<string, Cell> ErroDict { get; protected set; }

        private void InitPrivate()
        {
            InitAll();
        }
        protected virtual void InitAll()
        {
            InputCellColor = Color.FromArgb(255, 255, 255, 255);
            ErroDict = new Dictionary<string, Cell>();
            InputRangeDict = new Dictionary<string, ObjInputRowRange>();
            FontColorValidateDict = new Dictionary<Color, ObjValidateBase>();
            AddValidateByFontColor();
        }

        public void GenTemplate(ASPxSpreadsheet sheet, ASPxSpreadsheet templateSheet)
        {
            sheet.StylesPopupMenu.Style.CssClass = "righ-menu";
            CustomGenTemplate(sheet, templateSheet);
            _tempSpreadsheet = new ASPxSpreadsheet();
            _tempSpreadsheet.Document.Worksheets.Add(TEMP_SHEET);
            _tempSpreadsheet.Document.Worksheets[TEMP_SHEET].Visible = false;
            AddInputRange(sheet);
        }

        protected abstract void CustomGenTemplate(ASPxSpreadsheet sheet, ASPxSpreadsheet templateSheet);

        public void OnRequestCalculate(ASPxSpreadsheet sheet, ASPxGridView txtResult)
        {
            var dt = new DataTable();
            dt.Columns.Add("Error");
            dt.Columns.Add("RangeInfo");
            ClearDictErr();
            ValidateSheet(sheet);
            if (ErroDict.Count == 0)
            {
                CalculateSheet(sheet);
            }
            else
            {
                foreach (string r in ErroDict.Keys)
                {
                    DataRow dr = dt.NewRow();
                    dr["Error"] = "Cell [" + r + "] - Sheet [" + ErroDict[r].Worksheet.Name + "] " + ErroDict[r].Tag +
                                  Environment.NewLine;
                    dr["RangeInfo"] = r + "|" + ErroDict[r].Worksheet.Name;
                    dt.Rows.Add(dr);
                }
            }
            txtResult.DataSource = dt;
            txtResult.DataBind();
        }

        public virtual void ClearDictErr()
        {
            foreach (Cell c in ErroDict.Values)
            {
                //T/h ko phai cell loi
                if (!c.FillColor.Equals(Color.Red)) continue;
                c.FillColor = Color.White;
                c.Tag = null;
            }
            ErroDict.Clear();
        }

        protected void ValidateSheet(ASPxSpreadsheet sheet)
        {
            ValidateByFontColor(sheet);
            CustomValidateSheet(sheet);
        }

        protected virtual void CustomValidateSheet(ASPxSpreadsheet sheet)
        {
        }

        /// <summary>
        ///     Override to Add Validate custom class for each excel template
        /// </summary>
        protected abstract void AddValidateByFontColor();

        protected abstract void AddInputRange(ASPxSpreadsheet sheet);

        protected virtual void ValidateByFontColor(ASPxSpreadsheet sheet)
        {
            foreach (ObjInputRowRange o in InputRangeDict.Values)
            {
                Worksheet cSheet = sheet.Document.Worksheets[o.SheetName];
                for (int i = o.TopRowIndex; i <= o.BottomRowIndex; i++)
                {
                    for (int j = o.LeftColIndex; j <= o.RightColIndex; j++)
                    {
                        Cell cell = cSheet.Cells[i, j];
                        //t/h ko phai la input cell
                        if (cell.FillColor.Equals(InputCellColor)) continue;
                        Color color = cell.Font.Color;
                        if (FontColorValidateDict.ContainsKey(color))
                        {
                            if (!FontColorValidateDict[color].IsValidate(cell.DisplayText))
                            {
                                AddErrCell(cSheet, cell, FontColorValidateDict[color].ErrorStr);
                            }
                        }
                    }
                }
            }
        }

        protected virtual void AddErrCell(Worksheet sheet, Cell c, string err)
        {
            string cellName = sheet.Columns[c.ColumnIndex].Heading + (c.RowIndex + 1);
            c.FillColor = Color.Red;
            c.Tag = err;
            ErroDict[cellName] = c;
        }

        protected virtual void CalculateSheet(ASPxSpreadsheet sheet)
        {
        }

        public virtual void SpreadSheetCallbackEvent(ASPxSpreadsheet sheet, object sender, CallbackEventArgsBase e)
        {
            string str = e.Parameter.Substring(0, 2);
            string data = e.Parameter.Remove(0, 2);
            ;
            switch (str)
            {
                case "cv": //Paste data event
                    int index = data.IndexOf('|');
                    string cellData = data.Substring(0, index);
                    string pasteData = data.Remove(0, index + 1);
                    string[] indexCell = cellData.Split(':');
                    int activeCellColumnIndex = Convert.ToInt32(indexCell[0]);
                    int activeCellRowIndex = Convert.ToInt32(indexCell[1]);
                    ProcPaseData(sheet, pasteData.TrimEnd('\n').TrimEnd('\r'), activeCellColumnIndex, activeCellRowIndex);
                    break;
                case "rb": //ribbon event
                    string[] rbData = data.Split(':');
                    string item = rbData[0];
                    string para = rbData[1];
                    ProcRibbonEvent(sheet, item, para);
                    break;
                case "er": //ribbon grid event
                    string erCellData = data.Substring(0, data.IndexOf('|'));
                    string erSheet = data.Substring(erCellData.Length + 1, data.Length - erCellData.Length - 1);
                    sheet.Document.Worksheets.ActiveWorksheet = sheet.Document.Worksheets[erSheet];
                    sheet.Document.Worksheets.ActiveWorksheet.ScrollToRow(
                        sheet.Document.Worksheets.ActiveWorksheet.Cells[erCellData].RowIndex);
                    break;
            }
        }

        protected virtual void ProcRibbonEvent(ASPxSpreadsheet sheet, string item, string parameter)
        {
            Worksheet atvSheet = sheet.Document.Worksheets.ActiveWorksheet;
            switch (item)
            {
                case "undo":
                    sheet.Document.History.Undo();
                    break;
                case "redo":
                    sheet.Document.History.Redo();
                    break;
                case "insertRow":
                    int insRowIndex = Convert.ToInt32(parameter);
                    if (InputRangeDict.Values.Any(o => o.IsInsertRow(insRowIndex, atvSheet.Name)))
                    {
                        atvSheet.Rows.Insert(insRowIndex);
                        atvSheet.Rows[insRowIndex].CopyFrom(atvSheet.Rows[insRowIndex + 1], PasteSpecial.Formats);
                        atvSheet.Selection = atvSheet.Rows[insRowIndex];
                        atvSheet.ScrollToRow(insRowIndex);
                        foreach (ObjInputRowRange o in InputRangeDict.Values)
                        {
                            o.UpdateInsert(insRowIndex, atvSheet.Name);
                        }
                    }
                    break;
                case "deleteRow":
                    int delRowIndex = Convert.ToInt32(parameter);
                    if (
                        InputRangeDict.Values.Any(
                            o => o.Contain(delRowIndex, atvSheet.Name) && o.IsDeleteRow(delRowIndex, atvSheet.Name)))
                    {
                        atvSheet.Rows.Remove(delRowIndex);
                        atvSheet.Selection = atvSheet.Rows[delRowIndex];
                        atvSheet.ScrollToRow(delRowIndex);
                        foreach (ObjInputRowRange o in InputRangeDict.Values)
                        {
                            o.UpdateDelete(delRowIndex, atvSheet.Name);
                        }
                    }
                    break;
            }
        }

        protected virtual void ParseCellValue(string s,Cell cell)
        {
            if (!string.IsNullOrEmpty(cell.NumberFormat))
            {
                double v;
                if (double.TryParse(s, out v))
                {
                    cell.Value = v;
                }
                else
                {
                    DateTime d;
                    if (DateTime.TryParseExact(s, cell.NumberFormat, CultureInfo.CurrentCulture,
                        DateTimeStyles.None, out d))
                    {
                        cell.Value = d;
                    }
                    else
                        cell.Value = s;
                }
            }
            else
            {
                cell.Value = s;
            }
        }

        protected virtual void ProcPaseData(ASPxSpreadsheet aspSheet, string data, int activeCellColumnIndex,
            int activeCellRowIndex)
        {
            Worksheet tempSheet = _tempSpreadsheet.Document.Worksheets[TEMP_SHEET];
            Worksheet activeSheet = aspSheet.Document.Worksheets.ActiveWorksheet;
            tempSheet.CopyFrom(activeSheet);
            string[] rowdata = data.Split('\n');
            foreach (string t in rowdata)
            {
                string[] celldata = t.Split('\t');
                int tempCell = activeCellColumnIndex;
                foreach (string s in celldata)
                {
                    Cell cell = tempSheet.Cells[activeCellRowIndex, tempCell];
                    if (!cell.Protection.Locked)
                    {
                        ParseCellValue(s, cell);
                    }

                    tempCell++;
                }
                activeCellRowIndex++;
            }
            activeSheet.CopyFrom(tempSheet);
            tempSheet.GetUsedRange().Clear();
            tempSheet.Charts.Clear();
            tempSheet.Pictures.Clear();
        }
    }

    public enum SpreadSheetTemplateType
    {
        Base = 0,
        UnconfinedCompressionTest,
        QuTemplate,
    }
}